package com.igoal.utils;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.security.MessageDigest;

public class CodecUtils {
	
	private static final int cFilterChar;
	private static final String charList;
	private static final String unsafeString;
	private static final String hexVals[];
	
	/**
	 * 
	 */
	static {
		cFilterChar = '=';
		charList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		
		//unsafeString = "\"<>%\\^[]`\+\$\,";
		unsafeString = "\"<>\\^[]`+$,";				
		hexVals = new String[] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
	              				"A", "B", "C", "D", "E", "F"};

	}
	
	/**
	 * Encodes a byte array to a string
	 * 
	 * @param data
	 * @return
	 */
	public static String encode(byte[] data) {
		int c;
		StringBuffer ret = new StringBuffer(4);
		for (int i = 0; i < data.length; i++) {
			c = (data[i] >> 2) & 0x3f;
			ret.append(charList.charAt(c));
			c = (data[i] << 4) & 0x3f;
			if (++i < data.length)
				c = c | (data[i] >> 4) & 0x0f;
			ret.append(charList.charAt(c));
			if (i < data.length) {
				c = (data[i] << 2) & 0x3f;
				if (++i < data.length)
					c = c | (data[i] >> 6) & 0x03;
				ret.append(charList.charAt(c));
			} else {
				++i;
				ret.append((char) cFilterChar);
			}
			if (i < data.length) {
				c = data[i] & 0x3f;
				ret.append(charList.charAt(c));
			} else {
				ret.append((char) cFilterChar);
			}
		}
		return ret.toString();
	}
	
	/**
	 * Encode an String using UTF-8 standard
	 * @param sValue  
	 * @return null if sValue null or cannot encode
	 */
	public static String encode(String sValue){
		if(sValue == null){
			return null;
		}
		try{
			String ret = URLEncoder.encode(sValue, "UTF-8");
			ret = ret.replaceAll("\\+", "%20");
			return ret;
		}catch (UnsupportedEncodingException e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * Decode an String using <code>enc</code> standard
	 * @param sValue 
	 * @param enc UTF-8 or ISO-xxx-x or something that java supported
	 * @return null if <code>sUrl</code> is null or cannot decode with specified standard
	 */
	public static String decode(String sValue, String enc){
		if(sValue == null){
			return null;
		}
		try{
			sValue = sValue.replaceAll("%20", "+");
			return URLDecoder.decode(sValue, enc);
		}catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}
	
	/**
	 * Decodes an encoded string to a byte array
	 * 
	 * @param sData
	 * @return
	 */
	@Deprecated
	public static byte[] decode(String sData) {
		
		char[] data = sData.toCharArray();
		int c;
		int c1;
		StringBuffer ret = new StringBuffer(3);
		for (int i = 0; i < data.length; i++) {
			c = charList.indexOf(data[i]);
			++i;
			c1 = charList.indexOf(data[i]);
			c = ((c << 2) | ((c1 >> 4) & 0x3));
			ret.append((char) c);
			if (++i < data.length) {
				c = data[i];
				if (cFilterChar == c)
					break;
				c = charList.indexOf((char) c);
				c1 = ((c1 << 4) & 0xf0) | ((c >> 2) & 0xf);
				ret.append((char) c1);
			}
			if (++i < data.length) {
				c1 = data[i];
				if (cFilterChar == c1)
					break;
				c1 = charList.indexOf((char) c1);
				c = ((c << 6) & 0xc0) | c1;
				ret.append((char) c);
			}
		}
		return (changeToBytes(ret.toString()));
	}

	/**
	 * Converts a string to a byte array
	 * 
	 * @param str
	 * @return
	 */
	private static byte[] changeToBytes(String str) {
		byte[] b = new byte[str.length()];
		for (int i = 0; i < b.length; i++)
			b[i] = (byte) str.charAt(i);
		return (b);
	}

	/**
	 * Converts a given String to URL Format
	 * Returns original Input once finding a non-ISO-8859-1 character
	 * 
	 * @param sInput
	 * @return
	 */
	public static String encodeURL(final String sInput) {
	
		int iLength = sInput.length();
		String sReturn = "";
		
		for(int i=0; i<iLength; i++) {
			
			Character currentChar = sInput.charAt(i);
			
			if(currentChar.hashCode() < 255) {// hack to eliminate the rest of unicode from this	    	  
	     
	    	  if(isUnsafe(currentChar)) {
	    		  sReturn += convert(currentChar);	    		  
	    	  } else { 
	    		  sReturn += currentChar;
	    	  }
	    	  
	      }
	      else {// woopsie! restore.

				sReturn = sInput;
				break;
				
			}

		}

		return sReturn;
	}

	
	/**
	 * Checks to see if a char is URL unsafe.
	 * Returns a boolean result. True = unsafe, False = safe
	 * 
	 * @param compareChar
	 * @return
	 */
	
	private static boolean isUnsafe(final Character compareChar) {
		
		if (unsafeString.indexOf(compareChar) == -1
				&& compareChar.hashCode() > 32
				&& compareChar.hashCode() < 123) {
			return false;	//found no unsafe chars, return false
		} else {
			return true;
		}
		
	}
	
	/**
	 * Converts a given char to url hex form
	 * 
	 * @param convertChar
	 * @return
	 */
	private static String convert(final Character convertChar) {
		return  "%" + decToHex(convertChar.hashCode(), 16);
	}
	
	/**
	 * Part of the hex-ifying functionality
	 * 
	 * @param num
	 * @param radix
	 * @return
	 */
	private static String decToHex(int num, final int radix) {
		
		String hexString = "";
		
		while (num >= radix) {
			int temp = num % radix;
			num = (int)Math.floor(num/radix);
			hexString += hexVals[temp];
		}
		
		hexString += hexVals[num];
		
		return reversal(hexString);
		
	}
	
	/**
	 * Part of the hex-ifying functionality
	 * 
	 * @param sInput
	 * @return
	 */

	private static String reversal(final String sInput) {
		
		int iLength = sInput.length();
		String sOutput = "";

		for (int i = 0; i<iLength; i++) {
			sOutput += sInput.substring(iLength - i - 1, iLength - i);
		}

		return sOutput;
		
	}

	/**
	 *Reverts a URL back to String Format
	 **/
//	public static String decodeURL(final String sInput) {
//	    while (backlen > 0)
//        {
//          lastpercent = val.lastIndexOf("%");
//          if (lastpercent != -1) // we found a % char. Need to handle
//             {
//               // everything *after* the %
//               frag = val.substring(lastpercent+1,val.length);
//               // re-assign val to everything *before* the %
//               val  = val.substring(0,lastpercent);
//               if (frag.length >= 2) // end contains unencoded
//                  {
//                  //  alert ("frag is greater than or equal to 2");
//                    encval = frag.substring(0,2);
//                    newStr = frag.substring(2,frag.length) + newStr;
//                    //convert the char here. for now it just doesn't add it.
//                    if ("01234567890abcdefABCDEF".indexOf(encval.substring(0,1)) != -1 &&
//                        "01234567890abcdefABCDEF".indexOf(encval.substring(1,2)) != -1)
//                       {
//                        encval = String.fromCharCode(parseInt(encval, 16)); // hex to base 10
//                        newStr = encval + newStr; // prepend the char in
//                       }
//                    // if so, convert. Else, ignore it.
//                  }
//               // adjust length of the string to be examined
//               backlen = lastpercent;
//              // alert ("backlen at the end of the found % if is: " + backlen);
//             }
//         else { newStr = val + newStr; backlen = 0; } // if there is no %, just leave the value as-is
//        } // end while
//
//		
//		
//	}

// public static String encodeURL(String sInput) {
//		
//		String sReturn = "";
//		
//		/*
//		 * analyze the URL
//		 */
//		String sProtocol = sInput.substring(0, sInput.indexOf("//") + 1);
//		
//		String sLink = "";		
//		if(sInput.indexOf("?") > 0) {			
//			sLink = sInput.substring(sInput.indexOf("//") + 2, sInput.indexOf("?"));
//		} else {
//			sLink = sInput.substring(sInput.indexOf("//") + 2);
//		}
//		
//		String sParams = "";
//		if(sInput.indexOf("?") > 0) {
//			sParams = sInput.substring(sInput.indexOf("?"));
//		}
//		
//		//encode the LINK
//		String arr[] = sLink.split("/");
//		
//		for(String sElement : arr) {			
//			sReturn += "/" + URLEncoder.encode(sElement);			
//		}
//		
//		/*
//		 * 
//		 */
//		
//		sReturn = sProtocol + sReturn + sParams;
//		
//	}
	
	/**
	 * Returns an Hexadecimal String of MD5
	 * 
	 * @param alg
	 * @param buffer
	 * @return
	 * @throws Exception
	 */
	public static String hashCode(final String alg, final byte buffer[])throws Exception {
		
		MessageDigest md = MessageDigest.getInstance(alg);
		byte hc[] = md.digest(buffer);
		
		// convert an byte array to hexadecimal string
		StringBuffer sb = new StringBuffer();
		
	     for( int i=0 ; i<hc.length ; i++ ) {
	         String sTemp = "0" + Integer.toHexString( (0xff & hc[i]) );
	         sb.append(sTemp.substring(sTemp.length()-2));
	     }
	     
	     return sb.toString();
	     
		//BASE64Encoder encoder = new BASE64Encoder();
		//return encoder.encode(hc);
	}
}
